/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florent Guillaume
*/
package org.eclipse.ecr.core.storage.sql.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map.Entry;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.storage.Credentials;
import org.eclipse.ecr.core.storage.StorageException;
import org.eclipse.ecr.core.storage.sql.Mapper;
import org.eclipse.ecr.core.storage.sql.Model;
import org.eclipse.ecr.core.storage.sql.ModelSetup;
import org.eclipse.ecr.core.storage.sql.RepositoryBackend;
import org.eclipse.ecr.core.storage.sql.RepositoryDescriptor;
import org.eclipse.ecr.core.storage.sql.RepositoryImpl;
import org.eclipse.ecr.core.storage.sql.Session.PathResolver;
import org.eclipse.ecr.core.storage.sql.jdbc.dialect.Dialect;
import org.eclipse.ecr.runtime.api.Framework;
/**
* JDBC Backend for a repository.
*/
public class JDBCBackend implements RepositoryBackend {
private static final Log log = LogFactory.getLog(JDBCBackend.class);
private RepositoryImpl repository;
private XADataSource xadatasource;
private Dialect dialect;
private SQLInfo sqlInfo;
private ClusterNodeHandler clusterNodeHandler;
private JDBCConnectionPropagator connectionPropagator;
public JDBCBackend() {
connectionPropagator = new JDBCConnectionPropagator();
}
@Override
public void initialize(RepositoryImpl repository) throws StorageException {
this.repository = repository;
RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor();
// instantiate the datasource
String className = repositoryDescriptor.xaDataSourceName;
Class<?> klass;
try {
klass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new StorageException("Unknown class: " + className, e);
}
Object instance;
try {
instance = klass.newInstance();
} catch (Exception e) {
throw new StorageException(
"Cannot instantiate class: " + className, e);
}
if (!(instance instanceof XADataSource)) {
throw new StorageException("Not a XADataSource: " + className);
}
xadatasource = (XADataSource) instance;
// set JavaBean properties on the datasource
for (Entry<String, String> entry : repositoryDescriptor.properties.entrySet()) {
String name = entry.getKey();
Object value = Framework.expandVars(entry.getValue());
if (name.contains("/")) {
// old syntax where non-String types were explicited
name = name.substring(0, name.indexOf('/'));
}
// transform to proper JavaBean convention
if (Character.isLowerCase(name.charAt(1))) {
name = Character.toLowerCase(name.charAt(0))
+ name.substring(1);
}
try {
BeanUtils.setProperty(xadatasource, name, value);
} catch (Exception e) {
log.error(String.format("Cannot set %s = %s", name, value));
}
}
}
/**
* {@inheritDoc}
* <p>
* Opens a connection to get the dialect and finish initializing the
* {@link ModelSetup}.
*/
@Override
public void initializeModelSetup(ModelSetup modelSetup)
throws StorageException {
try {
XAConnection xaconnection = xadatasource.getXAConnection();
Connection connection = null;
try {
connection = xaconnection.getConnection();
dialect = Dialect.createDialect(connection,
repository.getBinaryManager(),
repository.getRepositoryDescriptor());
} finally {
if (connection != null) {
connection.close();
}
xaconnection.close();
}
} catch (SQLException e) {
throw new StorageException(e);
}
modelSetup.materializeFulltextSyntheticColumn = dialect.getMaterializeFulltextSyntheticColumn();
}
/**
* {@inheritDoc}
* <p>
* Creates the {@link SQLInfo} from the model and the dialect.
*/
@Override
public void initializeModel(Model model) throws StorageException {
sqlInfo = new SQLInfo(model, dialect);
}
@Override
public Mapper newMapper(Model model, PathResolver pathResolver,
Credentials credentials, boolean create) throws StorageException {
Mapper mapper = createMapper(model, pathResolver);
if (create) {
if (repository.getRepositoryDescriptor().noDDL) {
log.info("Skipping database creation");
} else {
// first connection, initialize the database
mapper.createDatabase();
}
// create cluster mapper if needed
RepositoryDescriptor repositoryDescriptor = repository.getRepositoryDescriptor();
if (repositoryDescriptor.clusteringEnabled) {
log.info("Clustering enabled with "
+ repositoryDescriptor.clusteringDelay
+ " ms delay for repository: " + repository.getName());
// use the mapper that created the database as cluster mapper
clusterNodeHandler = new ClusterNodeHandler(mapper,
repositoryDescriptor);
mapper = createMapper(model, pathResolver);
}
}
return mapper;
}
protected JDBCMapper createMapper(Model model, PathResolver pathResolver)
throws StorageException {
return new JDBCMapper(model, pathResolver, sqlInfo, xadatasource,
clusterNodeHandler, connectionPropagator);
}
@Override
public void shutdown() throws StorageException {
if (clusterNodeHandler != null) {
clusterNodeHandler.close();
}
}
}